package com.harmony.pilot.knowledge.service;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Git服务
 * 用于增量扫描，获取变更文件列表
 */
@Slf4j
@Service
public class GitService {
    
    @Value("${openharmony.code.base-path:../ohcode}")
    private String codeBasePath;
    
    /**
     * 获取自上次扫描后的变更文件
     */
    public List<String> getChangedFiles(String lastCommitHash) {
        List<String> changedFiles = new ArrayList<>();
        
        try {
            File gitDir = findGitDirectory(new File(codeBasePath));
            if (gitDir == null) {
                log.warn("未找到Git仓库，无法进行增量扫描");
                return changedFiles;
            }
            
            try (Git git = Git.open(gitDir)) {
                Repository repository = git.getRepository();
                
                // 如果指定了上次的commit，比较差异
                if (lastCommitHash != null && !lastCommitHash.isEmpty()) {
                    ObjectId oldCommit = repository.resolve(lastCommitHash);
                    ObjectId headCommit = repository.resolve("HEAD");
                    
                    if (oldCommit != null && headCommit != null) {
                        changedFiles = getDiffFiles(git, oldCommit, headCommit);
                    }
                } else {
                    // 如果没有上次的commit，获取最近N个提交的变更
                    changedFiles = getRecentChangedFiles(git, 10);
                }
            }
            
        } catch (Exception e) {
            log.error("获取Git变更文件失败", e);
        }
        
        return changedFiles;
    }
    
    /**
     * 获取两个commit之间的差异文件
     */
    private List<String> getDiffFiles(Git git, ObjectId oldCommit, ObjectId newCommit) 
            throws IOException, GitAPIException {
        
        List<String> changedFiles = new ArrayList<>();
        
        try (RevWalk walk = new RevWalk(git.getRepository())) {
            RevCommit oldRevCommit = walk.parseCommit(oldCommit);
            RevCommit newRevCommit = walk.parseCommit(newCommit);
            
            AbstractTreeIterator oldTree = prepareTreeParser(walk, oldRevCommit);
            AbstractTreeIterator newTree = prepareTreeParser(walk, newRevCommit);
            
            List<DiffEntry> diffs = git.diff()
                    .setOldTree(oldTree)
                    .setNewTree(newTree)
                    .call();
            
            for (DiffEntry diff : diffs) {
                String filePath = diff.getNewPath();
                // 只包含ADD、MODIFY、RENAME类型，排除DELETE
                if (diff.getChangeType() != DiffEntry.ChangeType.DELETE) {
                    changedFiles.add(filePath);
                }
            }
        }
        
        return changedFiles.stream()
                .filter(this::isCodeFile)
                .collect(Collectors.toList());
    }
    
    /**
     * 获取最近N个提交的变更文件
     */
    private List<String> getRecentChangedFiles(Git git, int commitCount) 
            throws GitAPIException, IOException {
        
        List<String> allChangedFiles = new HashSet<>().stream().collect(Collectors.toList());
        
        Iterable<RevCommit> commits = git.log()
                .setMaxCount(commitCount)
                .call();
        
        try (RevWalk walk = new RevWalk(git.getRepository())) {
            RevCommit prevCommit = null;
            
            for (RevCommit commit : commits) {
                if (prevCommit != null) {
                    List<String> diffFiles = getDiffFiles(git, prevCommit.getId(), commit.getId());
                    allChangedFiles.addAll(diffFiles);
                }
                prevCommit = walk.parseCommit(commit.getId());
                walk.markStart(prevCommit);
            }
        }
        
        return allChangedFiles.stream()
                .distinct()
                .filter(this::isCodeFile)
                .collect(Collectors.toList());
    }
    
    /**
     * 准备TreeParser
     */
    private AbstractTreeIterator prepareTreeParser(RevWalk walk, RevCommit commit) 
            throws IOException {
        
        CanonicalTreeParser treeParser = new CanonicalTreeParser();
        try {
            treeParser.reset(walk.getObjectReader(), commit.getTree().getId());
        } catch (IOException e) {
            log.error("准备TreeParser失败", e);
            throw e;
        }
        walk.dispose();
        return treeParser;
    }
    
    /**
     * 查找Git目录
     */
    private File findGitDirectory(File directory) {
        File current = directory;
        while (current != null) {
            File gitDir = new File(current, ".git");
            if (gitDir.exists() && gitDir.isDirectory()) {
                return gitDir;
            }
            current = current.getParentFile();
        }
        return null;
    }
    
    /**
     * 判断是否为代码文件
     */
    private boolean isCodeFile(String filePath) {
        if (filePath == null) {
            return false;
        }
        
        String lowerPath = filePath.toLowerCase();
        return lowerPath.endsWith(".h") || 
               lowerPath.endsWith(".hpp") || 
               lowerPath.endsWith(".cpp") || 
               lowerPath.endsWith(".cc") || 
               lowerPath.endsWith(".c") ||
               lowerPath.endsWith(".gn") ||
               lowerPath.endsWith(".gni");
    }
    
    /**
     * 获取当前HEAD的commit hash
     */
    public String getCurrentCommitHash() {
        try {
            File gitDir = findGitDirectory(new File(codeBasePath));
            if (gitDir == null) {
                return null;
            }
            
            try (Git git = Git.open(gitDir)) {
                ObjectId headId = git.getRepository().resolve("HEAD");
                return headId != null ? headId.getName() : null;
            }
        } catch (Exception e) {
            log.error("获取当前commit hash失败", e);
            return null;
        }
    }
    
    /**
     * 转换相对路径为绝对路径
     */
    public String toAbsolutePath(String relativePath) {
        Path basePath = Paths.get(codeBasePath);
        Path filePath = basePath.resolve(relativePath).normalize();
        return filePath.toString();
    }
}

